/*	--- 使用StampedLock --- 

Stamped  adj.  有邮戳的
validate  v.  确认,证实,批准

-------------------------------

前面介绍的ReadWriteLock可以解决多线程同时读，但只有一个线程能写的问题。


如果我们深入分析ReadWriteLock，会发现它有个潜在的问题：
	
	如果有线程正在读，写线程需要等待读线程释放锁后才能获取写锁，即读的过程中不允许写，这是一种悲观的读锁。


-------------


要进一步提升‘并发执行效率’，Java 8引入了新的读写锁：StampedLock。

StampedLock 和 ReadWriteLock 相比，改进之处在于：读的过程中也允许获取写锁后写入！

这样一来，我们读的数据就可能不一致，所以，需要一点额外的代码来判断读的过程中是否有写入，这种‘读锁’是一种【乐观锁】。

【乐观锁】的意思就是: 乐观地估计读的过程中大概率不会有写入，因此被称为【乐观锁】。

反过来，【悲观锁】则是读的过程中拒绝有写入，也就是写入必须等待。显然乐观锁的并发效率更高，但一旦有小概率的写入导致读取的数据不一致，需要能检测出来，再读一遍就行。

我们来看例子：  */
public class Point {
	private final StampedLock stampedLock = new StampedLock();

	private double x;
	private double y;

	public void move(double deltaX, double deltaY) {
		long stamp = stampedLock.writeLock(); // 获取写锁
	
		try {
			x += deltaX;
			y += deltaY;
		} finally {
			stampedLock.unlockWrite(stamp); // 释放写锁
		}
	}

	public double distanceFromOrigin() {
		long stamp = stampedLock.tryOptimisticRead(); 
			// Optimistic  adj.  乐观的
			// return 版本号 stamp

		// 注意! 下面2行代码不是原子操作
		// 假设x,y = ( 100,200 )

		double currentX = x;
		// 此处已读取到x = 100, 但x,y可能被写线程修改为(300,400)

		double currentY = y;
		// 此处已读取到y，如果没有写入，读取是正确的(100,200)
        // 如果有写入，读取是错误的(100,400)

		if (!stampedLock.validate(stamp)) { 
		// validate v. 确认; (validate验证(stamp)的版本号)
		// 检查【乐观读锁】后是否有其他写锁发生

			stamp = stampedLock.readLock(); // 获取一个悲观读锁

			try {
				currentX = x;
				currentY = y;
			} finally {
				stampedLock.unlockRead(stamp); // 释放悲观读锁
			}
		}

		return Math.sqrt(currentX * currentX + currentY * currentY);
	}
}


/*
和ReadWriteLock相比，写入的加锁是完全一样的，不同的是读取。


注意到首先我们通过tryOptimisticRead()获取一个乐观读锁，并返回版本号。


接着进行'读取'，读取完成后，我们通过validate()去验证版本号，如果在读取过程中没有写入，版本号不变，验证成功，我们就可以放心地继续后续操作。


如果在读取过程中有写入，版本号会发生变化，验证将失败。


在失败的时候，我们再通过获取悲观读锁再次读取。


由于写入的概率不高，程序在绝大部分情况下可以通过乐观读锁获取数据，极少数情况下使用悲观读锁获取数据。

-------------------------------------

可见，StampedLock把读锁细分为【乐观读】和【悲观读】，能进一步提升‘并发效率’。


但这也是有代价的：一是代码更加复杂，二是StampedLock是不可重入锁，不能在一个线程中反复获取同一个锁。


StampedLock还提供了更复杂的将【悲观读锁】升级为【写锁】的功能，它主要使用在if-then-update的场景：
	即先读，如果读的数据 满足 条件，就返回，
			如果读的数据 不满足 条件，再尝试写。

*/





/*---------------------------------------------------


#	---- 使用StampedLock の 小结 ----


1. StampedLock提供了【乐观读锁】，可取代ReadWriteLock以进一步提升‘并发性能’；

2. StampedLock是【不可重入锁】。


*/



























